{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "ddc5f583",
   "metadata": {},
   "source": [
    "# 单动作多智能体实例：辩论\n",
    "\n",
    "这是一个展示如何设计多个智能体并促进它们之间互动的例子，我们将模拟代表正方和反方的智能体共同合作会怎样，这样的组合可能会导致一些充满睿智的交流，辅助我们对一个议题做出更好的决策。\n",
    "\n",
    "metaGPT官网的实例代码详见：\n",
    "\n",
    "[https://github.com/geekan/MetaGPT/blob/main/examples/debate.py](https://github.com/geekan/MetaGPT/blob/main/examples/debate.py)\n",
    "\n",
    "本课进行了一些改编，便于我们用在自己的实际工作中，为我们日常决策提供一个良好的参考。以下的代码推荐在jupyter notebook的代码格子中运行，以便更好地观察到输出结果。\n",
    "\n",
    "辩论分三个步骤设定：\n",
    "\n",
    "1. 定义一个具有发言行为的辩手角色，详见前期课程\n",
    "2. 处理辩手之间的通信，也就是让正方听反方说话，反之亦然\n",
    "3. 初始化两个辩手实例，正方和反方，创建一个带有环境的团队，并使它们能够相互交互\n",
    "\n",
    "**导入模块**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "e441fc28",
   "metadata": {},
   "outputs": [],
   "source": [
    "import asyncio\n",
    "from typing import Any\n",
    "\n",
    "from metagpt.actions import Action, UserRequirement\n",
    "from metagpt.logs import logger\n",
    "from metagpt.roles import Role\n",
    "from metagpt.schema import Message\n",
    "from metagpt.team import Team"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "beebf8a9",
   "metadata": {},
   "source": [
    "**定义动作**\n",
    "\n",
    "首先，我们需要定义一个Action。这是一个辩论场景，所以让我们将其命名为SpeakAloud"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "35449ffb",
   "metadata": {},
   "outputs": [],
   "source": [
    "class SpeakAloud(Action):\n",
    "    \"\"\"Action: 清楚地阐述自己的观点\"\"\"\n",
    "\n",
    "    PROMPT_TEMPLATE: str = \"\"\"\n",
    "    ## 背景\n",
    "    设想你是 {name}, 你在与 {opponent_name}进行辩论.\n",
    "    ## 辩论历史记录\n",
    "    前面的轮次:\n",
    "    {context}\n",
    "    ## 轮到你了\n",
    "    现在轮到你了，\n",
    "    你应该紧密回应对手的最新论点，\n",
    "    阐述你的立场，捍卫你的论据，\n",
    "    并攻击对手的论点，\n",
    "    构思一个深思熟虑的反驳，你将争论：\n",
    "    \"\"\"\n",
    "    name: str = \"SpeakAloud\"\n",
    "\n",
    "    async def run(self, context: str, name: str, opponent_name: str):\n",
    "        prompt = self.PROMPT_TEMPLATE.format(context=context, name=name, opponent_name=opponent_name)\n",
    "        # logger.info(prompt)\n",
    "\n",
    "        rsp = await self._aask(prompt)\n",
    "\n",
    "        return rsp"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "756be3f2",
   "metadata": {},
   "source": [
    "**定义角色**\n",
    "\n",
    "定义一个通用的Role，称为Debator\n",
    "\n",
    "set\\_actions使我们的Role拥有我们刚刚定义的SpeakAloud动作。我们还使用\\_watch监视了SpeakAloud和 UserRequierment，因为我们希望每个辩手关注来自对手的SpeakAloud消息，以及来自用户的 UserRequirement(人类指令）。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "62f5c9d2",
   "metadata": {},
   "outputs": [],
   "source": [
    "class Debator(Role):\n",
    "    name: str = \"\"\n",
    "    profile: str = \"\"\n",
    "    opponent_name: str = \"\"\n",
    "\n",
    "    def __init__(self, **data: Any):\n",
    "        super().__init__(**data)\n",
    "        self.set_actions([SpeakAloud])\n",
    "        self._watch([UserRequirement, SpeakAloud])\n",
    "\n",
    "    async def _observe(self) -> int:\n",
    "        await super()._observe()\n",
    "        # accept messages sent (from opponent) to self, disregard own messages from the last round\n",
    "        self.rc.news = [msg for msg in self.rc.news if msg.send_to == {self.name}]\n",
    "        return len(self.rc.news)\n",
    "\n",
    "    async def _act(self) -> Message:\n",
    "        logger.info(f\"{self._setting}: to do {self.rc.todo}({self.rc.todo.name})\")\n",
    "        todo = self.rc.todo  # An instance of SpeakAloud\n",
    "\n",
    "        memories = self.get_memories()\n",
    "        context = \"\\n\".join(f\"{msg.sent_from}: {msg.content}\" for msg in memories)\n",
    "        # print(context)\n",
    "\n",
    "        rsp = await todo.run(context=context, name=self.name, opponent_name=self.opponent_name)\n",
    "\n",
    "        msg = Message(\n",
    "            content=rsp,\n",
    "            role=self.profile,\n",
    "            cause_by=type(todo),\n",
    "            sent_from=self.name,\n",
    "            send_to=self.opponent_name,\n",
    "        )\n",
    "        self.rc.memory.add(msg)\n",
    "\n",
    "        return msg"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "60f7fb78",
   "metadata": {},
   "source": [
    "上面重写了\\_observe函数，我们使每个辩手听取对手论点。这一步很重要，因为在环境中将会有来自正方和反方的 \"SpeakAloud 消息\"（由SpeakAloud触发的Message）。 我们不希望反方处理自己上一轮的 \"SpeakAloud 消息\"，而是处理来自正方的消息，反之亦然。\n",
    "\n",
    "\n",
    "最后，我们使每个辩手能够向对手发送反驳的论点。在这里，我们从消息历史中构建一个上下文，使Debator运行他拥有的SpeakAloud动作，并使用反驳论点内容创建一个新的Message。请注意，我们定义每个Debator将把Message发送给他的对手\n",
    "\n",
    "\n",
    "**创建团队并添加角色**\n",
    "\n",
    "建立一个Team将角色动作组合起来，我们将通过将我们的指令（作为UserRequirement）发送给正方，让他先开始。如果你想让反方先说话，将send\\_to设置为 \"Fan\"。\n",
    "\n",
    "运行main函数就可以看到他们之间的对话！"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "17929b25",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "2025-02-26 17:39:40.264 | INFO     | metagpt.team:invest:90 - Investment: $3.0.\n",
      "2025-02-26 17:39:40.271 | INFO     | __main__:_act:18 - Zheng(正方): to do SpeakAloud(SpeakAloud)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "中小企业应该训练自己的大模型。\n",
      "\n",
      "## 反驳 Fan 的论点\n",
      "\n",
      "尊敬的 Fan，感谢您提出的观点。然而，我认为中小企业训练自己的大模型并不是一个明智的选择，以下是我的几个理由：\n",
      "\n",
      "1. **高昂的成本**：大模型的训练需要大量的计算资源和存储空间，这对于中小企业来说是一笔巨大的开销。许多中小企业可能没有足够的资金来承担这样的成本。\n",
      "\n",
      "2. **技术挑战**：训练大模型需要专业的技术知识和经验。中小企业可能缺乏这样的专业人才，导致项目难以顺利进行。\n",
      "\n",
      "3. **维护和更新**：大模型需要定期维护和更新，以保持其性能和准确性。中小企业可能没有足够的人力和资源来处理这些工作。\n",
      "\n",
      "4. **数据隐私和安全**：大模型通常需要处理大量的数据，这涉及到数据隐私和安全问题。中小企业可能没有足够的安全措施来保护这些数据。\n",
      "\n",
      "5. **资源分散**：中小企业将资源投入到大模型训练中，可能会分散其原本可以用于其他关键业务领域的资源。\n",
      "\n",
      "相反，我认为中小企业应该采取以下策略：\n",
      "\n",
      "- **合作共享**：中小企业可以与其他企业或研究机构合作，共享资源，共同训练大模型，从而降低成本。\n",
      "\n",
      "- **专注于特定领域**：中小企业可以专注于其核心业务领域，利用现有的小型模型或第三方的大模型来满足其需求。\n",
      "\n",
      "- **持续学习**：中小企业应投资于员工的技术培训，提高其利用现有技术和工具的能力。\n",
      "\n",
      "总之，我认为中小企业应该谨慎考虑是否训练自己的大模型，并寻找更合适、成本效益更高的解决方案。"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "2025-02-26 17:39:53.253 | WARNING  | metagpt.utils.cost_manager:update_cost:49 - Model glm-4-flash not found in TOKEN_COSTS.\n",
      "2025-02-26 17:39:53.260 | INFO     | __main__:_act:18 - Fan(反方): to do SpeakAloud(SpeakAloud)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "尊敬的 Zheng，我理解您的担忧，但我想指出，尽管存在这些挑战，中小企业训练自己的大模型仍然有其独特的优势和价值。以下是我对您观点的反驳：\n",
      "\n",
      "1. **成本问题**：确实，大模型的训练成本高昂，但这并不意味着中小企业无法从中受益。首先，随着技术的进步，云计算服务的成本正在下降，中小企业可以通过租用云服务来分摊成本。其次，大模型一旦训练完成，其产生的效益可以长期持续，从长远来看，这种投资是值得的。\n",
      "\n",
      "2. **技术挑战**：虽然技术挑战存在，但中小企业可以通过以下方式克服：一是与高校和研究机构合作，利用他们的专业知识；二是通过内部培养，逐步建立自己的技术团队。此外，随着人工智能技术的普及，越来越多的在线资源和教程可以帮助中小企业克服技术障碍。\n",
      "\n",
      "3. **维护和更新**：确实，大模型的维护和更新需要持续投入。然而，这同样是一个机会，让中小企业在维护过程中不断学习和优化自己的模型，提高其竞争力。\n",
      "\n",
      "4. **数据隐私和安全**：数据隐私和安全是任何企业都需要考虑的问题。中小企业可以通过加强内部管理、采用先进的数据加密技术以及遵守相关法律法规来确保数据安全。\n",
      "\n",
      "5. **资源分散**：虽然大模型训练可能会分散资源，但这是为了获得更大的长期利益。中小企业可以通过合理规划资源，确保核心业务不受影响。\n",
      "\n",
      "我的观点是，中小企业训练自己的大模型可以带来以下好处：\n",
      "\n",
      "- **增强竞争力**：通过拥有自己的大模型，中小企业可以在市场竞争中占据有利地位。\n",
      "- **创新驱动**：大模型可以激发中小企业在产品和服务上的创新，推动业务发展。\n",
      "- **数据所有权**：拥有自己的大模型意味着中小企业可以完全控制自己的数据，避免数据泄露的风险。\n",
      "\n",
      "综上所述，尽管存在挑战，中小企业训练自己的大模型仍然是一个值得考虑的战略选择。通过合理规划和管理，这些挑战是可以被克服的"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "2025-02-26 17:40:08.212 | WARNING  | metagpt.utils.cost_manager:update_cost:49 - Model glm-4-flash not found in TOKEN_COSTS.\n",
      "2025-02-26 17:40:08.219 | INFO     | __main__:_act:18 - Zheng(正方): to do SpeakAloud(SpeakAloud)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "。\n",
      "尊敬的 Fan，感谢您的反驳和补充。我理解您的观点，但我想进一步强调以下几点，以巩固我的立场：\n",
      "\n",
      "1. **成本效益分析**：您提到云计算服务的成本正在下降，这是事实。然而，中小企业通常没有足够的预算来承担初期的高昂成本，即使长期来看可能具有成本效益。此外，云计算服务的费用仍然可能占据中小企业预算的很大一部分，这可能会限制它们在其他关键领域的投资。\n",
      "\n",
      "2. **技术依赖与合作**：虽然与高校和研究机构合作是一个可行的方案，但这本身也带来了一系列挑战。中小企业可能需要花费额外的时间和资源来建立和维护这些合作关系，而且合作成果的归属和知识产权问题也需要明确。此外，技术依赖可能导致中小企业在技术更新和迭代时处于被动地位。\n",
      "\n",
      "3. **维护和更新的可持续性**：大模型的维护和更新确实是一个持续的过程，但这并不意味着中小企业能够持续投入。随着时间的推移，维护成本可能会超过模型带来的收益，尤其是当市场和技术环境发生变化时。\n",
      "\n",
      "4. **数据隐私和安全的风险**：尽管中小企业可以采取措施保护数据安全，但数据泄露的风险仍然存在。一旦发生数据泄露，对中小企业的声誉和业务可能造成不可逆转的损害。\n",
      "\n",
      "5. **资源分散的后果**：您提到中小企业可以通过合理规划资源来确保核心业务不受影响。然而，实际情况是，资源分散可能会导致中小企业在多个领域都难以达到最佳效果，从而影响整体竞争力。\n",
      "\n",
      "针对您的观点，我提出以下反驳：\n",
      "\n",
      "- **增强竞争力**：虽然拥有自己的大模型可能增强竞争力，但中小企业可能没有足够的能力和资源来开发出具有竞争力的模型。此外，市场竞争激烈，中小企业可能无法在短时间内通过大模型获得显著优势。\n",
      "\n",
      "- **创新驱动**：创新需要时间和资源，而中小企业可能没有足够的资源来支持持续的创新活动。依赖大模型可能只是短期内的创新，而不是长期的创新驱动。\n",
      "\n",
      "- **数据所有权**：虽然数据所有权对于中小企业来说很重要，但拥有大模型并不一定意味着完全控制数据。数据的使用和共享可能受到法律和商业协议的限制。\n",
      "\n",
      "综上所述，我认为中小企业在当前阶段训练自己的大模型可能并不是最佳选择。相反，它们应该专注于其核心业务，利用现有技术和资源，同时关注行业趋势，为未来的技术投资做好准备。通过这样的策略，中小企业可以更加稳健地发展，同时避免不必要的风险。"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "2025-02-26 17:40:29.399 | WARNING  | metagpt.utils.cost_manager:update_cost:49 - Model glm-4-flash not found in TOKEN_COSTS.\n",
      "2025-02-26 17:40:29.406 | INFO     | __main__:_act:18 - Fan(反方): to do SpeakAloud(SpeakAloud)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "尊敬的 Zheng，我感谢您的深入分析和补充论点。您的担忧确实体现了中小企业在技术投资方面的谨慎态度。然而，我认为您的某些论点可能过于悲观，以下是我对您最新论点的反驳：\n",
      "\n",
      "1. **成本效益分析**：您提到中小企业可能没有足够的预算来承担初期的高昂成本。确实，初期成本可能较高，但正如我之前提到的，随着云计算服务的普及和成本下降，中小企业可以通过灵活的付费模式来分摊成本。此外，大模型带来的长期效益，如提高效率、降低运营成本和增强创新能力，可能会抵消初期的高投入。\n",
      "\n",
      "2. **技术依赖与合作**：合作确实需要时间和资源，但这是中小企业成长和发展的必经之路。通过与高校和研究机构合作，中小企业不仅可以获得技术支持，还可以通过合作建立长期稳定的合作关系，这对于企业的可持续发展至关重要。\n",
      "\n",
      "3. **维护和更新的可持续性**：大模型的维护和更新确实是一个挑战，但这也是一个学习和适应的过程。通过持续的技术投入和人才培养，中小企业可以逐步提高自身的维护能力，确保模型的长期有效运行。\n",
      "\n",
      "4. **数据隐私和安全的风险**：数据安全和隐私是所有企业都需要面对的问题。中小企业可以通过建立完善的数据安全管理体系，以及采用最新的加密技术来降低风险。此外，遵守相关法律法规也是保护数据安全的重要手段。\n",
      "\n",
      "5. **资源分散的后果**：我同意资源分散可能会影响企业的整体竞争力，但关键在于如何平衡资源分配。通过合理的战略规划和资源管理，中小企业可以在保持核心业务稳定的同时，逐步投资于大模型训练。\n",
      "\n",
      "针对您的反驳，我提出以下观点：\n",
      "\n",
      "- **增强竞争力**：虽然中小企业可能没有立即获得显著优势，但通过拥有自己的大模型，它们可以逐步积累技术优势，并在未来市场竞争中占据有利地位。\n",
      "\n",
      "- **创新驱动**：创新确实需要时间和资源，但大模型可以为中小企业提供强大的创新工具。通过利用大模型，中小企业可以更快地探索新的业务模式和解决方案。\n",
      "\n",
      "- **数据所有权**：拥有大模型确实有助于中小企业控制数据，但这并不意味着数据无法共享或使用。通过合理的商业协议和数据共享机制，中小企业可以在保护数据安全的同时，实现数据的合理利用。\n",
      "\n",
      "总之，我认为中小企业在当前阶段训练自己的大模型是一个具有前瞻性和战略性的决策。通过合理规划和管理，这些挑战是可以被克服的，而大模型带来的长期利益将"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "2025-02-26 17:40:47.821 | WARNING  | metagpt.utils.cost_manager:update_cost:49 - Model glm-4-flash not found in TOKEN_COSTS.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "远远超过其带来的风险。\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "2025-02-26 17:40:47.827 | INFO     | __main__:_act:18 - Zheng(正方): to do SpeakAloud(SpeakAloud)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "尊敬的 Fan，我再次感谢您的深入讨论和反驳。您的观点提出了许多有价值的考虑，但我仍然坚持我的立场，以下是我对您最新论点的反驳：\n",
      "\n",
      "1. **成本效益分析**：您提到云计算服务的成本正在下降，并且可以通过灵活的付费模式分摊成本。然而，这种模式对于中小企业来说可能并不适用，因为它们可能没有足够的预算来支付初期的高额费用，更不用说持续的费用了。此外，云计算服务的成本可能会随着数据量的增加而增加，这对于中小企业来说是一个不确定的因素。\n",
      "\n",
      "2. **技术依赖与合作**：合作确实可以带来技术支持，但这也意味着中小企业需要依赖外部合作伙伴，这可能会带来合作不稳定性和不确定性。此外，合作可能需要长时间的谈判和资源投入，这对于资源有限的中小企业来说是一个挑战。\n",
      "\n",
      "3. **维护和更新的可持续性**：虽然持续的技术投入和人才培养可以提高维护能力，但这需要时间和资金，中小企业可能没有这样的资源。此外，技术的快速迭代可能导致之前的技术和模型迅速过时。\n",
      "\n",
      "4. **数据隐私和安全的风险**：即使采用最新的加密技术和遵守法律法规，数据泄露的风险仍然存在。对于中小企业来说，一旦发生数据泄露，可能会对它们的业务造成致命打击。\n",
      "\n",
      "5. **资源分散的后果**：您提到可以通过合理的战略规划和资源管理来平衡资源分配。然而，实际上，中小企业可能没有足够的能力来做出这样的战略规划，这可能导致资源分配不当，影响核心业务的稳定性。\n",
      "\n",
      "针对您的观点，我提出以下反驳：\n",
      "\n",
      "- **增强竞争力**：虽然拥有自己的大模型可能有助于中小企业在长期内增强竞争力，但这需要时间和资源，中小企业可能没有这样的耐心和资源。此外，市场竞争激烈，中小企业可能无法在短时间内通过大模型获得显著优势。\n",
      "\n",
      "- **创新驱动**：大模型可能为创新提供工具，但创新不仅仅是技术问题，还需要市场洞察、商业模式和执行力。中小企业可能在这些方面缺乏优势。\n",
      "\n",
      "- **数据所有权**：即使拥有大模型，中小企业也可能无法完全控制数据，尤其是在数据共享和商业合作中。此外，数据所有权并不等同于数据利用能力。\n",
      "\n",
      "综上所述，我认为中小企业在当前阶段训练自己的大模型可能并不是最佳选择。它们应该更加专注于其核心业务，利用现有技术和资源，同时关注行业趋势，为未来的技术投资做好准备。通过这样的策略，中小企业可以更加稳健地发展，同时避免不必要的风险。"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "2025-02-26 17:41:10.081 | WARNING  | metagpt.utils.cost_manager:update_cost:49 - Model glm-4-flash not found in TOKEN_COSTS.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n"
     ]
    }
   ],
   "source": [
    "async def debate(idea: str, investment: float = 3.0, n_round: int = 5):\n",
    "    \"\"\"开启辩论过程，充分论证观点\"\"\"\n",
    "    Zheng = Debator(name=\"Zheng\", profile=\"正方\", opponent_name=\"Fan\")\n",
    "    Fan = Debator(name=\"Fan\", profile=\"反方\", opponent_name=\"Zheng\")\n",
    "    team = Team()\n",
    "    team.hire([Zheng, Fan])\n",
    "    team.invest(investment)\n",
    "    team.run_project(idea, send_to=\"Zheng\") \n",
    "    await team.run(n_round=n_round)\n",
    "\n",
    "\n",
    "async def main(idea: str, investment: float = 3.0, n_round: int = 5):\n",
    "    \"\"\"\n",
    "    :param idea: 辩论主题\n",
    "    :param investment: 监控花费的token，不要超出\n",
    "    :param n_round: 辩论的最大轮次\n",
    "    :return:\n",
    "    \"\"\"\n",
    "#     if platform.system() == \"Windows\":\n",
    "#         asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())\n",
    "    await debate(idea, investment, n_round)\n",
    "\n",
    "\n",
    "await main(idea=\"中小型企业应该训练自己的大模型\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0e260e19",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.11"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
